using System;
using System.Linq;
using Microsoft.Cci;
using Microsoft.Cci.MutableCodeModel;
using Cci = Microsoft.Cci.MutableCodeModel;
using Mutable = Microsoft.Cci.MutableCodeModel;
using System.Diagnostics.Contracts;


namespace ReactiveLang.AssemblyRewriter
{
    public class ReactiveMethodRewriter
    {
        PdbReader _pdb;
        IMetadataHost _host;
        ReactiveCodeAssemblyInfo _asmInfo;
        IMethodDefinition _method;

        void NotSupported(IObjectWithLocations stmt, Error error)
        {
            var location = stmt.Locations.Concat(_method.Locations).FirstOrDefault() as ISourceLocation;
            var err = new ReactiveErrorMessage(location, error);
            _host.ReportError(err);
        }

        public ReactiveMethodRewriter(IMetadataHost host, PdbReader pdb)
        {
            _pdb = pdb;
            _host = host;
            _asmInfo = new ReactiveCodeAssemblyInfo(host.NameTable);
        }

        public MethodDefinition Visit(MethodDefinition method)
        {
            Contract.Requires(method != null);
            Contract.Assert(_method == null);
            try
            {
                _method = method;
                method.Body = Visit(method.Body);
                return method;
            }
            finally
            {
                _method = null;
            }
        }
        IMethodBody Visit(IMethodBody method)
        {
            var mutable = new MethodBody();
            mutable.Copy(method, null);
            var locals = mutable.LocalVariables;
            for (int i = 0; i < locals.Count; i++)
                locals[i] = Visit(locals[i]);
            var methodVariable = new LocalDefinition
            {
                Name = _host.NameTable.GetNameFor("__ReactiveCode.ReactiveMethod__"),
                Type = new GenericTypeInstanceReference
                {
                    GenericType = new Mutable.NamespaceTypeReference
                    {
                        ContainingUnitNamespace = _asmInfo.ReactiveCodeNamespace,
                        Name = _host.NameTable.GetNameFor("ReactiveMethod")
                    }
                }
            };

            var srcMethodBody = (SourceMethodBody) Microsoft.Cci.ILToCodeModel.Decompiler.GetCodeModelFromMetadataModel(_host, mutable, _pdb);
            var root = (BlockStatement) srcMethodBody.Block;
            var originalBody = root.Statements.ToList();
            root.Statements.Clear();
            foreach(var local in srcMethodBody.LocalVariables)
            {
                if (local.IsConstant)
                    continue;

                root.Statements.Add(new LocalDeclarationStatement
                {
                    LocalVariable = local,
                    InitialValue = new CreateObjectInstance
                    {
                        Type = local.Type,
                        MethodToCall = new Mutable.MethodReference {Type = local.Type, Name = _host.NameTable.GetNameFor(".ctor")}
                    }
                });
            }

            root.Statements.Add(new LocalDeclarationStatement
            {
                
            });


            var newMethodInstance = new CreateObjectInstance
            {
                
            };
            return srcMethodBody;
        }

        ILocalDefinition Visit(ILocalDefinition loc)
        {
            if (loc.CustomModifiers.Any() || loc.IsPinned)
            {
                NotSupported(loc, Error.NotSupportedVariable);
            }
            if (loc.IsConstant)
                return loc;

            var mutable = new LocalDefinition();
            mutable.Copy(loc, null);
            mutable.Type = new GenericTypeInstanceReference
            {
                GenericType = _asmInfo.VariableType,
                GenericArguments = { loc.Type }
            };
            return mutable;
        }

        Cci.Statement Visit(Cci.Statement statement)
        {
            throw new NotImplementedException();
        }
    }
}